<!-- FaceLogin.vue -->
<template>
  <div class="face-login">
    <div class="login-container" v-if="!isVideoStarted">
      <h2>人脸识别登录</h2>
      <input 
        type="text" 
        v-model="username" 
        placeholder="请输入用户名"
        @input="validateUsername" 
      />
      <button 
        :disabled="!isUsernameValid" 
        @click="startFaceRecognition"
      >
        开始人脸识别
      </button>
    </div>
    
    <div id="videoContainer" v-show="isVideoStarted">
      <video 
        ref="video" 
        width="720" 
        height="560" 
        autoplay 
        muted
      ></video>
      <canvas ref="canvas"></canvas>
    </div>

    <div 
      class="status-div"
      :style="{ backgroundColor: statusBgColor }"
    >
      {{ statusMessage }}
    </div>
  </div>
</template>

<script>
import * as faceapi from 'face-api.js'
import { getUserRoleAPI } from "@/request/api";
import { mapMutations } from "vuex";

export default {
  name: 'FaceLogin',
  data() {
    return {
      processTimer: null,
      username: '',
      role:'',
      isUsernameValid: false,
      isVideoStarted: false,
      statusMessage: '正在加载模型...',
      statusBgColor: 'rgba(0,0,0,0.7)',
      referenceDescriptor: null,
      matchStartTime: null,
      isMatching: false,
      displaySize: {
        width: 720,
        height: 560
      },
    }
  },
  async mounted() {
    await this.loadModels()
  },
  methods: {
    ...mapMutations(["saveUsername", "saverole"]),
    async loadModels() {
      try {
        await Promise.all([
          faceapi.nets.ssdMobilenetv1.loadFromUri('./models'),
          faceapi.nets.tinyFaceDetector.loadFromUri('./models'),
          faceapi.nets.faceLandmark68Net.loadFromUri('./models'),
          faceapi.nets.faceRecognitionNet.loadFromUri('./models'),
          faceapi.nets.faceExpressionNet.loadFromUri('./models')
        ])
        this.statusMessage = '模型加载完成，请输入用户名'
      } catch (error) {
        this.statusMessage = '模型加载失败'
        console.error('Error loading models:', error)
      }
    },

    validateUsername() {
      this.isUsernameValid = this.username.trim().length > 0
    },

    async loadImage(username) {
      return new Promise((resolve, reject) => {
        const img = new Image()
        img.crossOrigin = "Anonymous";  // 设置 CORS 属性
        img.src = `http://localhost:3000/faces/${username}.jpg`;  // 使用后端提供的URL
        img.onload = () => resolve(img)
        img.onerror = (e) => {
          this.statusMessage = '未找到用户照片，请确认用户名是否正确'
          reject(e)
        }
      })
    },

    async getRefDescriptor(username) {
      try {
        const refImage = await this.loadImage(username)
        this.statusMessage = '正在处理用户照片...'
        const detection = await faceapi
          .detectSingleFace(refImage)
          .withFaceLandmarks()
          .withFaceDescriptor()
        
        if (detection) {
          this.statusMessage = '用户照片处理完成'
          return detection.descriptor
        } else {
          this.statusMessage = '用户照片中未检测到人脸'
          return null
        }
      } catch (e) {
        this.statusMessage = '处理用户照片时出错'
        return null
      }
    },

    async startVideo() {
      try {
        const stream = await navigator.mediaDevices.getUserMedia({ video: {} })
        this.$refs.video.srcObject = stream
        this.$refs.video.addEventListener('play', this.onPlay)
      } catch (err) {
        console.error(err)
        this.statusMessage = '摄像头启动失败，请检查权限设置'
      }
    },

    async startFaceRecognition() {
      if (!this.username.trim()) {
        this.statusMessage = '请输入用户名'
        return
      }

      this.statusMessage = '正在加载用户照片...'
      this.referenceDescriptor = await this.getRefDescriptor(this.username)

      if (this.referenceDescriptor) {
        this.isVideoStarted = true
        await this.startVideo()
      } else {
        this.statusMessage = '加载用户照片失败，请检查用户名是否正确'
      }
    },

    async onPlay() {
      const canvas = this.$refs.canvas
      faceapi.matchDimensions(canvas, this.displaySize)

      this.processTimer = setInterval(async () => {
        const detections = await faceapi
          .detectAllFaces(this.$refs.video, new faceapi.TinyFaceDetectorOptions())
          .withFaceLandmarks()
          .withFaceDescriptors()
          .withFaceExpressions()

        const resizedDetections = faceapi.resizeResults(detections, this.displaySize)

        const ctx = canvas.getContext('2d')
        ctx.clearRect(0, 0, canvas.width, canvas.height)
        
        faceapi.draw.drawDetections(canvas, resizedDetections)
        faceapi.draw.drawFaceLandmarks(canvas, resizedDetections)
        faceapi.draw.drawFaceExpressions(canvas, resizedDetections)

        if (this.referenceDescriptor && detections.length > 0) {
          this.processDetections(detections, ctx)
        } else {
          this.resetMatchingStatus()
        }
      }, 100)
    },

    processDetections(detections, ctx) {
      detections.forEach(detection => {
        const distance = faceapi.euclideanDistance(detection.descriptor, this.referenceDescriptor)
        const similarity = 1 - distance
        ctx.font = '24px Arial'

        if (similarity > 0.62) {
          this.handleSuccessfulMatch(detection, similarity, ctx)
        } else {
          this.handleFailedMatch(detection, similarity, ctx)
        }
      })
    },

    handleSuccessfulMatch(detection, similarity, ctx) {
      ctx.fillStyle = 'green'
      ctx.fillText(
        `匹配成功 ${(similarity * 100).toFixed(1)}%`,
        detection.detection.box.x,
        detection.detection.box.y - 10
      )
      
      this.statusMessage = `用户 ${this.username} 匹配成功！相似度: ${(similarity * 100).toFixed(1)}%`
      this.statusBgColor = 'rgba(0,255,0,0.7)'

      if (!this.isMatching) {
        this.matchStartTime = Date.now()
        this.isMatching = true
      }

      if (this.isMatching && (Date.now() - this.matchStartTime) > 1500) {
        this.handleLoginSuccess()
      }
    },

    handleFailedMatch(detection, similarity, ctx) {
      ctx.fillStyle = 'red'
      ctx.fillText(
        `未匹配 ${(similarity * 100).toFixed(1)}%`,
        detection.detection.box.x,
        detection.detection.box.y - 10
      )
      
      this.statusMessage = `未匹配，相似度: ${(similarity * 100).toFixed(1)}%`
      this.statusBgColor = 'rgba(255,0,0,0.7)'
      this.resetMatchingStatus()
    },

    resetMatchingStatus() {
      this.isMatching = false
      this.matchStartTime = null
    },
    cleanupResources() {
      // 清理定时器
      if (this.processTimer) {
          clearInterval(this.processTimer);
          this.processTimer = null;
      }
      
      // 安全地清理视频流
      try {
          if (this.$refs.video && this.$refs.video.srcObject) {
              const stream = this.$refs.video.srcObject;
              const tracks = stream.getTracks();
              tracks.forEach(track => track.stop());
              this.$refs.video.srcObject = null;
          }
      } catch (err) {
          console.warn('清理视频资源时出错:', err);
      }
    },
    async handleLoginSuccess() {
      try {
        // 获取用户角色
        const roleResponse = await getUserRoleAPI({ username: this.username }); // 传入对象格式的参数
        
        if (roleResponse.code === 200) {
          this.role = roleResponse.data.role;
          
          // 显示成功消息
          this.$message({
            message: "人脸识别验证通过！",
            type: "success"
          });
          console.log(this.username);
          console.log(this.role);
          // 使用vuex保存用户名和角色（转换为字符串格式保存）
          this.saveUsername(this.username);
          const roleText = this.role === 1 ? "管理员" : "用户";
          this.saverole(roleText);
          // 安全地清理资源
          this.cleanupResources();

          // 将用户信息存储到 localStorage
          localStorage.setItem('userRole', this.role);
          localStorage.setItem('username', this.username);
          // 将 Token 存储到 localStorage
          localStorage.setItem('book_token', roleResponse.data.token);
          // 跳转到主页
          this.$router.push({ path: '/home' });

        } else {
          throw new Error(roleResponse.message || '获取用户角色失败');
        }
      } catch (error) {
        console.error('Face login error:', error);
        this.$message.error(error.message || "登录过程中发生错误");
      }
    }
  },
  beforeUnmount() {
      // 清理 interval
      if (this.processTimer) {
          clearInterval(this.processTimer);
          this.processTimer = null;
      }

      // 停止视频流
      if (this.$refs.video && this.$refs.video.srcObject) {
          const stream = this.$refs.video.srcObject;
          stream.getTracks().forEach(track => track.stop());
          this.$refs.video.srcObject = null;
      }
  }
}
</script>

<style scoped>
.face-login {
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  background-color: #f0f2f5;
}

#videoContainer {
  position: relative;
  width: 720px;
  height: 560px;
}

video {
  position: absolute;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

canvas {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

.login-container {
  background: white;
  padding: 20px;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0,0,0,0.1);
  text-align: center;
}

input {
  padding: 8px 12px;
  margin: 10px 0;
  border: 1px solid #d9d9d9;
  border-radius: 4px;
  width: 200px;
  font-size: 14px;
}

input:focus {
  border-color: #1890ff;
  outline: none;
  box-shadow: 0 0 0 2px rgba(24,144,255,0.2);
}

button {
  padding: 10px 20px;
  font-size: 16px;
  background-color: #1890ff;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  margin: 10px;
}

button:hover {
  background-color: #40a9ff;
}

button:disabled {
  background-color: #d9d9d9;
  cursor: not-allowed;
}

.status-div {
  position: fixed;
  top: 20px;
  left: 20px;
  padding: 10px;
  color: white;
  border-radius: 5px;
  transition: background-color 0.3s ease;
}
</style>